package com.wsoft.bpm.component;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wsoft.bpm.bean.ApprovalChainNode;
import com.wsoft.bpm.bean.DynamicApprovalChain;
import com.wsoft.bpm.dto.FlowElementChainKeyMap;
import com.wsoft.bpm.entity.ActWorkflowConfigEntity;
import com.wsoft.bpm.query.node.ListenerQuery;
import com.wsoft.bpm.query.node.TaskQuery;
import com.wsoft.bpm.service.IActWorkflowConfigService;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 支持动态审批链的流程图绘制器
 *
 * @Author zhonglj
 * @Date 2025/3/10 14:07
 */
@Log4j2
public class DynamicChainDiagramGenerator extends DefaultProcessDiagramGenerator {
    private static Pattern pattern = Pattern.compile("task,'(.*?)'");
    private RuntimeService runtimeService = SpringUtil.getBean(RuntimeService.class);
    private HistoryService historyService = SpringUtil.getBean(HistoryService.class);

    private String procInstId;

    public DynamicChainDiagramGenerator(String procInstId) {
        this.procInstId = procInstId;
    }

    @SneakyThrows
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel,
                                       List<String> highLightedActivities,
                                       List<String> highLightedFlows,
                                       String activityFontName,
                                       String labelFontName,
                                       String annotationFontName,
                                       boolean generateDefaultDiagram,
                                       String defaultDiagramImageFileName) {
        // 创建深拷贝（使用Activiti官方方法）
        BpmnModel workingBpmnModel = cloneBpmnModel(bpmnModel);
        // 获取流程定义
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        String processDefinitionId = Opt.ofNullable(processInstance).map(ProcessInstance::getProcessDefinitionId)
                .orElseGet(() -> historicProcessInstance.getProcessDefinitionId());

        // 查询配置json表数据
        IActWorkflowConfigService configService = SpringUtil.getBean(IActWorkflowConfigService.class);
        Map<String, String> actIdToJsonMap = configService.list(
                        Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                                .eq(ActWorkflowConfigEntity::getProcDefId, processDefinitionId)
                ).stream()
                .collect(Collectors.toMap(ActWorkflowConfigEntity::getActId, ActWorkflowConfigEntity::getJson));


        // 扩展点：处理动态审批链
        List<FlowElementChainKeyMap> approvalChainTasks = workingBpmnModel.getMainProcess().getFlowElements().stream()
                .map(flowElement -> {
                    FlowElementChainKeyMap map = new FlowElementChainKeyMap();
                    map.setElement(flowElement);
                    if (flowElement instanceof UserTask) {
                        String json = actIdToJsonMap.get(flowElement.getId());
                        if (StrUtil.isNotBlank(json)) {
                            TaskQuery query = JSONUtil.toBean(json, TaskQuery.class);
                            Optional<ListenerQuery> find = query.getListeners().stream()
                                    .filter(e -> StrUtil.contains(e.getValue(), "createDynamicApprovalChain"))
                                    .findFirst();
                            if (find.isPresent()) {
                                String value = find.get().getValue();
                                if (value != null) {
                                    value = value.replaceAll(" ", "");
                                    Matcher matcher = pattern.matcher(value);
                                    if (matcher.find()) {
                                        map.setKey(matcher.group(1));
                                    }
                                }

                            }
                        }
                    }

                    return map;
                })
                .filter(map -> StrUtil.isNotBlank(map.getKey()))
                .collect(Collectors.toList());  // 先收集到临时列表
        approvalChainTasks.forEach(e -> processDynamicTask(e, workingBpmnModel, highLightedActivities, highLightedFlows));

        return super.generateDiagram(workingBpmnModel, highLightedActivities, highLightedFlows, activityFontName, labelFontName, annotationFontName, generateDefaultDiagram, defaultDiagramImageFileName);
    }

    private void processDynamicTask(FlowElementChainKeyMap map, BpmnModel bpmnModel,
                                    List<String> highLightedActivities,
                                    List<String> highLightedFlows) {
        FlowElement element = map.getElement();
        String chainKey = map.getKey();

        // 查询当前动态审批链
        DynamicApprovalChain chain = null;
        try {
            chain = runtimeService.getVariable(procInstId, chainKey, DynamicApprovalChain.class);
        } catch (Exception e) {
            HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(procInstId)
                    .variableName(chainKey)
                    .singleResult();
            chain = DynamicApprovalChain.class.cast(historicVariableInstance.getValue());
        }

        if (chain == null) {
            return;
        }

        // 获取动态节点起始节点的图形信息
        GraphicInfo baseInfo = bpmnModel.getGraphicInfo(element.getId());
        Process process = bpmnModel.getMainProcess();

        // 继承基础节点尺寸
        double nodeWidth = baseInfo.getWidth();
        double nodeHeight = baseInfo.getHeight();

        // 基于基础节点位置布局
        double currentX = baseInfo.getX();
        // 下移50px
        double currentY = baseInfo.getY() + baseInfo.getHeight() + 50.0;
        for (ApprovalChainNode node : chain.getNodes()) {
            GraphicInfo info = new GraphicInfo();
            info.setX(currentX);
            info.setY(currentY);
            info.setWidth(nodeWidth);
            info.setHeight(nodeHeight);
            // 创建虚拟活动节点
            UserTask virtualActivity = createVirtualActivity(node);
            if (chain.approved(node)) {
                highLightedActivities.add(virtualActivity.getId());
            }
            process.addFlowElement(virtualActivity);
            bpmnModel.addGraphicInfo(virtualActivity.getId(), info);

            // 节点间距50px
            currentY += nodeHeight + 50.0;
        }

        // 生成连接线
        createDynamicSequenceFlows(bpmnModel, chain, element, highLightedFlows);
    }

    private UserTask createVirtualActivity(ApprovalChainNode node) {
        UserTask virtualTask = new UserTask();
        virtualTask.setId("VIRTUAL_" + node.getBusinessId());
        virtualTask.setName(node.getApprovalName());
        return virtualTask;
    }

    private void createDynamicSequenceFlows(BpmnModel bpmnModel, DynamicApprovalChain chain,
                                            FlowElement element, List<String> highLightedFlows) {
        ArrayList<ApprovalChainNode> nodes = chain.getNodes();
        // 防御性校验
        if (CollUtil.isEmpty(nodes) || element == null) return;

        Process mainProcess = bpmnModel.getMainProcess();
        String baseElementId = element.getId();

        // 创建基础节点到首个虚拟节点的连线
        createConnectionFlow(bpmnModel, mainProcess, baseElementId, nodes.get(0), chain, highLightedFlows);

        // 创建虚拟节点之间的连线
        for (int i = 0; i < nodes.size() - 1; i++) {
            createConnectionFlow(bpmnModel, mainProcess, getVirtualNodeId(nodes.get(i)), nodes.get(i + 1), chain, highLightedFlows);
        }
    }

    // 创建单个连接线（提取公共逻辑）
    private void createConnectionFlow(BpmnModel bpmnModel,
                                      Process process,
                                      String sourceId,
                                      ApprovalChainNode targetNode,
                                      DynamicApprovalChain chain,
                                      List<String> highLightedFlows) {
        String targetId = getVirtualNodeId(targetNode);
        // 参数校验
        GraphicInfo sourceInfo = getGraphicInfoSafe(bpmnModel, sourceId);
        GraphicInfo targetInfo = getGraphicInfoSafe(bpmnModel, targetId);
        if (sourceInfo == null || targetInfo == null) {
            return;
        }

        // 创建序列流
        SequenceFlow flow = buildSequenceFlow(sourceId, targetId);
        process.addFlowElement(flow);

        // 高亮连线
        if (chain.approved(targetNode)) {
            highLightedFlows.add(flow.getId());
        }

        // 绑定图形信息
        linkFlowToModel(bpmnModel, flow, sourceInfo, targetInfo);

        // 维护流向关系
        linkOutgoingFlow(bpmnModel, sourceId, flow);
    }

    // 构建序列流对象
    private SequenceFlow buildSequenceFlow(String sourceId, String targetId) {
        SequenceFlow flow = new SequenceFlow(sourceId, targetId);
        flow.setId("FLOW_" + sourceId.replace("VIRTUAL_", ""));
        return flow;
    }

    // 绑定连线图形信息
    private void linkFlowToModel(BpmnModel bpmnModel, SequenceFlow flow,
                                 GraphicInfo sourceInfo, GraphicInfo targetInfo) {
        List<GraphicInfo> waypoints = new ArrayList<>(2);

        // 起点坐标（源节点底部中心）
        waypoints.add(createWaypoint(
                sourceInfo.getX() + sourceInfo.getWidth() / 2,
                sourceInfo.getY() + sourceInfo.getHeight()
        ));

        // 终点坐标（目标节点顶部中心）
        waypoints.add(createWaypoint(
                targetInfo.getX() + targetInfo.getWidth() / 2,
                targetInfo.getY()
        ));

        bpmnModel.addFlowGraphicInfoList(flow.getId(), waypoints);
    }

    private String getVirtualNodeId(ApprovalChainNode node) {
        return "VIRTUAL_" + node.getBusinessId();
    }

    private GraphicInfo getGraphicInfoSafe(BpmnModel bpmnModel, String elementId) {
        GraphicInfo info = bpmnModel.getGraphicInfo(elementId);
        if (info == null) {
            log.warn(StrUtil.format("未找到元素图形信息：{}", elementId));
        }
        return info;
    }

    private GraphicInfo createWaypoint(double x, double y) {
        GraphicInfo point = new GraphicInfo();
        point.setX(x);
        point.setY(y);
        return point;
    }

    private void linkOutgoingFlow(BpmnModel bpmnModel, String sourceId, SequenceFlow flow) {
        FlowElement sourceElement = bpmnModel.getFlowElement(sourceId);
        if (sourceElement instanceof FlowNode) {
            ((FlowNode) sourceElement).getOutgoingFlows().add(flow);
        }
    }

    // 修正克隆方法
    private BpmnModel cloneBpmnModel(BpmnModel original) throws XMLStreamException {
        BpmnXMLConverter converter = new BpmnXMLConverter();
        // 使用正确的XML转换方式
        byte[] xmlBytes = converter.convertToXML(original, StandardCharsets.UTF_8.name());

        InputStream stream = new ByteArrayInputStream(xmlBytes);
        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in = new InputStreamReader(stream, StandardCharsets.UTF_8);
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        return converter.convertToBpmnModel(xtr);
    }
}
